/**
 ******************************************************************************
 * @文件名           : fml_store.c
 * @作者             : mbl
 * @功能介绍         : 数据存储算法
 *	@日期             : 2024.06.19
 ******************************************************************************
 * @注意
 *
 *
 *
 ******************************************************************************
 *更改记录
 *日期 修改人 修改内容
 *
 *
 *
 ******************************************************************************
 **/
#include "string.h"
#include "stdlib.h"
#include "fml_store.h"
#include "fml_crc.h"
#include "dri_flash_sst.h"
/**
 * @函数名	FML_FlashDeviceRegister
 * @功能	    对于传入的FALSH设备进行合法性验证
 * @入口参数	flash 用户自定义外部FALSH信息
 * @返回值	无
 */
void FML_FlashDeviceRegister(FlashDevice_t *flash)
{
	// 名称为空
	assert(flash->flashName != NULL);
	// 最小操作尺寸大于总尺寸
	assert(flash->minEraseSize <= flash->totalSize);
	// 最小擦除尺寸为0
	assert(flash->minEraseSize != 0);
	// 最小操作吃醋呢为0
	assert(flash->minOperateSize != 0);
	// 总尺寸为0
	assert(flash->totalSize != 0);
	// flash地址越界超过uint32_t
	assert(flash->startAddr + flash->totalSize <= 0xFFFFFFFF);
	// flash起始地址和总大小越界
	assert(flash->totalSize <= 0xFFFFFFFF || flash->startAddr <= 0xFFFFFFFF);
	// 最小操作尺寸和擦除尺寸越界
	assert(flash->minEraseSize <= 0xFFFF || flash->minOperateSize <= 0xFFFF);

}
/**
 * @函数名	FML_PartInit
 * @功能	    初始化分区，计算分页数和页内数据区数量，检查分区合法性
 * @入口参数	part：分区实体 size:分区的数量
 * @返回值	无
 */
void FML_PartInit(Partition_t *part,uint8_t size)
{
	/* 1.检查分区数据合法性 */
	assert(size != 0);

	for(uint8_t i = 0; i < size; i++)
	{
		// 分区起始地址小于FLASH起始地址
		assert(part[i].partStartAddr >= part[i].partFlash->startAddr);
		// 分区起始地址超过FLASH分区范围
		assert(part[i].partStartAddr <= \
				(part[i].partFlash->startAddr + part[i].partFlash->totalSize));
		// 分区大小超过FLASH大小或分区大小为0
		assert(part[i].partSize <= part[i].partFlash->totalSize || part[i].partSize != 0);
		// 分区大小不在FLASH范围内
		assert(part[i].partStartAddr + part[i].partSize <= part[i].partFlash->startAddr + \
				part[i].partFlash->totalSize);
		// 向分区内写入的初始化数据大小为0
		assert(part[i].dataSize != 0);
		// 向分区内写入的初始化数据超过一页
		assert(part[i].dataSize <= part[i].partFlash->minEraseSize - sizeof(PageHeader_t));
		// 检查两个分区是够冲突
		if(i > 0)
		{
			assert(part[i].partStartAddr >= \
					(part[i-1].partStartAddr + part[i-1].partSize));
		}
		/* 2.计算页数量，和页内数据区数量 */
		// 分区之中包含多少个页面
		part[i].pageNum = part[i].partSize / part[i].partFlash->minEraseSize;
		// 计算一也之中包含多少个数据区(包含CRC)
		part[i].dataNum = (part[i].partFlash->minEraseSize - sizeof(PageHeader_t))/ \
						(part[i].dataSize + CRC_BIT);
		/* 3.调用外部FLASH初始化，初始化外部FLASH */
		part[i].partFlash->initFlashCB();
	}
}
/**
 * @函数名	FML_PartReset
 * @功能	    擦除分区每页数据，初始化表头，向初始页写入数据
 * @入口参数	part：分区实体
 * @返回值	无
 */
void FML_PartReset(Partition_t *part)
{
	PageHeader_t pageHead = { 0 };
	uint16_t crcValue = 0;
	uint32_t pageAddr = 0;
	uint8_t  crcValueBuff[part->dataSize + CRC_BIT];

	for (uint16_t i = 0; i < part->pageNum; ++i)
	{
		// 计算当前扇区的地址
		pageAddr = part->partStartAddr + i * part->partFlash->minEraseSize;

		// 擦除当前4KB扇区
		part->partFlash->eraseFlashCB(pageAddr , part->partFlash->minEraseSize);

		// 将页头标记为空并初始化
		pageHead.pageStatus = PAGE_FREE;

		// 将页头写入
		part->partFlash->writeFlashCB(pageAddr, (uint8_t*) &(pageHead.pageStatus), \
				sizeof(pageHead.pageStatus));
	}
	// 将页的第一个数据区标志为正在使用,并写入数据
	pageHead.pageStatus = PAGE_IN_USE;
	memset(pageHead.dataIndex, 0xFFFFFFFF, sizeof(pageHead.dataIndex));
	pageHead.dataIndex[0] = 0x7FFFFFFF;
	// CRC校验
	crcValue = Crc16(part->dataAddr, part->dataSize);

	memcpy(crcValueBuff, part->dataAddr, part->dataSize);
	crcValueBuff[part->dataSize] = crcValue & 0x00FF;
	crcValueBuff[part->dataSize + 1] = crcValue>>8;
	// 先更新数据在更新页头
	part->partFlash->writeFlashCB(part->partStartAddr + sizeof(pageHead), \
			crcValueBuff, part->dataSize+CRC_BIT);
	// 再更新表头
	part->partFlash->writeFlashCB(part->partStartAddr, \
					(uint8_t*)&(pageHead), sizeof(pageHead));

}
/**
 * @函数名		DataIndexRead
 * @功能	    	从分区内读出数据的当前地址,当前单元号
 * @入口参数		Part：分区对象 *dataAddr：数据地址  *dataSite：数据在页中位置
 * @返回值		错误码
 */
static FLASH_Err_t DataIndexRead(Partition_t Part, uint32_t *dataAddr, uint16_t *dataSite)
{
	PageHeader_t pageHead = {0};
	uint32_t indexStatus = 0 ;
	uint16_t findDataIndex = 0, findPageIndex = 0;

	/* 1：遍历分区找最后数据的地址 */
    for (uint16_t i = 0; i < Part.pageNum; i++)
    {
        // 计算当前扇区的地址
        uint32_t pageAddr = Part.partStartAddr + i * Part.partFlash->minEraseSize;

        //读取每一页的页头
        Part.partFlash->readFlashCB(pageAddr,(uint8_t*)&pageHead,sizeof(pageHead));
        if(pageHead.pageStatus != PAGE_IN_USE)
		{
			continue;
		}
		if(pageHead.dataIndex[0] == 0xFFFFFFFF)
		{
			return FLASH_READ_ERR;/* flash未配置 */
		}
		for(uint16_t j = 0; j < Part.dataNum; )
		{
			indexStatus = pageHead.dataIndex[j / DATA_INDEX_UNIN_SIZE];
			if(indexStatus)
			{
				if(indexStatus & (0x80000000 >> (j % DATA_INDEX_UNIN_SIZE)))
				{
					findDataIndex = j - 1;/* 最后数据的索引位置为索引区第一个为1位的上一位 */
					findPageIndex = i;	//当前页面数量
					break;
				}
				else
				{
					j++;
					/* 如果索引以达到数据区最大值，说明数据在最后一个数据区 */
					if(j == Part.dataNum)
					{
						findDataIndex = j - 1;
						findPageIndex = i;	//当前页面数量
						break;
					}
				}
			}
			else
			{

				j += DATA_INDEX_UNIN_SIZE;
				/* 如果索引以达到数据区最大值，说明数据在最后一个数据区 */
				if(j == Part.dataNum)
				{
					findDataIndex = j - 1;
					break;
				}
				/* 如果未使用索引位为0，提示未使用索引位异常 */
				if(j >= Part.dataNum)
				{
					return FLASH_READ_ERR;
				}
			}
		}

    }
    //数据地址 = 区域起始地址 + 页面数量*页面大小 + 页头大小 + 数据区数量*数据区大小
    *dataAddr = Part.partStartAddr + findPageIndex * Part.partFlash->minEraseSize \
    		   + sizeof(pageHead) + findDataIndex * (Part.dataSize + 2);
    //
    *dataSite = findDataIndex;
    return FLASH_NO_ERR;
}
/**
 * @函数名	FML_PartRead
 * @功能	    从分区内读出最新的实时数据
 * @入口参数	Part：分区对象 buffer：读出的数组
 * @返回值	错误码
 */
FLASH_Err_t FML_PartRead(Partition_t Part, uint8_t *buff)
{
	FLASH_Err_t readResult = FLASH_NO_ERR;
//	uint8_t tempBuff[Part.dataSize + CRC_BIT];
	uint8_t *tempBuff = malloc(sizeof(uint8_t)*(Part.dataSize + CRC_BIT));
	uint16_t crcValue = 0, dataSite = 0;
	uint32_t dataAddr = 0;

	/* 1：读出当前数据区域地址，计算数据的存放位置 */
	readResult = DataIndexRead(Part, &dataAddr, &dataSite);
	/* 2. 判断是否读出数据地址 */
	if(readResult != FLASH_NO_ERR)
	{
		free(tempBuff);
		return readResult;
	}
	/* 3：根据最后数据的地址，读出最后的数据进缓冲区 */
    Part.partFlash->readFlashCB(dataAddr, tempBuff, Part.dataSize + CRC_BIT);
	/* 4：缓冲区的数据进行校验，判断数据是否异常 */
    crcValue = Crc16(tempBuff, Part.dataSize);
    if(crcValue == (tempBuff[Part.dataSize] | (tempBuff[Part.dataSize+1]) << 8))
    {
    	memcpy(buff, tempBuff, Part.dataSize);
    	free(tempBuff);
    	return readResult;
    }
    else
    {
    	free(tempBuff);
    	return readResult;
    }
}

/**
 * @函数名		FML_PartWrite
 * @功能	    	从分区内写入最新的实时数据
 * @入口参数		Part：分区对象 buff: 写入的数组
 * @返回值		读取出来的数组
 */

FLASH_Err_t FML_PartWrite(Partition_t Part, uint8_t *buff)
{
	PageHeader_t pageHead = {0};
	FLASH_Err_t readResult = FLASH_NO_ERR;
	uint32_t writeDataAddr = 0,writePageAddr = 0;
	uint16_t badBlock = 0;
	uint32_t writeAddr = 0;
	uint16_t writeSite = 0;
	uint16_t crcAgoValue = 0, crcLaterValue = 0;
	uint16_t dataSite = 0;
	uint32_t dataAddr = 0;
	uint8_t  *crcValueBuff = malloc(sizeof(uint8_t)*(Part.dataSize + CRC_BIT));


	/* 1. 根据当前页表数据地址，确定写数据地址、写数据位置 */
	readResult = DataIndexRead(Part, &dataAddr, &dataSite);
	if(readResult != FLASH_NO_ERR)
	{
		free(crcValueBuff);
		return readResult;
	}
	writeSite = dataSite + 1;				// 写数据位置
	writeAddr = dataAddr + Part.dataSize + CRC_BIT;
	/* 2. 写数据之前进行CRC校验 */

Write:
	memcpy(crcValueBuff,buff,Part.dataSize);
	crcAgoValue = Crc16(crcValueBuff, Part.dataSize);
	crcValueBuff[Part.dataSize] = crcAgoValue & 0x00FF;
	crcValueBuff[Part.dataSize+1] = crcAgoValue>>8;
	/* 测试 */
//	if(debug++ < 120)
//	{
//		crcAgoValue = 0;
//	}
	/* 测试 */


	if(badBlock >= Part.dataNum * Part.pageNum)	// 如果坏块的数量超过整个分区报错
	{
		free(crcValueBuff);
		return FLASH_WRITE_ERR;
	}
	/* 3. 当前页面未写满，将数据写入 */
	if(writeSite < Part.dataNum)
	{
		writeDataAddr = writeAddr ;
		writePageAddr = writeAddr - (writeSite)*(Part.dataSize+CRC_BIT) - sizeof(pageHead);
		Part.partFlash->writeFlashCB(writeDataAddr, crcValueBuff, Part.dataSize+CRC_BIT);
		/* 3.1 读出数据再次进行CRC校验，获取读出后校验值 */
		Part.partFlash->readFlashCB(writeDataAddr, crcValueBuff, Part.dataSize + CRC_BIT);
		crcLaterValue = Crc16(crcValueBuff, Part.dataSize);
		// 逻辑出现问题应该 crcAgoValue = (crcValueBuff[Part.dataSize] | (crcValueBuff[Part.dataSize+1]) << 8)
		/* 3.2 根据前后CRC校验相等，更新索引表 */
		if(crcAgoValue == crcLaterValue)	// 数据正常
		{
			badBlock =  0;
			uint8_t unitNum = writeSite / DATA_INDEX_UNIN_SIZE;			// 当前数据距离上一次数据中间差多少个单元格
			uint16_t unitStatus = (writeSite % DATA_INDEX_UNIN_SIZE);	// 当前单元状态
			Part.partFlash->readFlashCB(writePageAddr, (uint8_t*)&pageHead,\
					sizeof(pageHead));
			/* 将写入数据之前的索引表状态位置置 0 */
			for(uint8_t j = 0; j < unitNum;j++)   //有多少单元格
			{
				pageHead.dataIndex[j] = 0;
			}

			pageHead.dataIndex[unitNum] = pageHead.dataIndex[unitNum] & \
					(0xFFFFFFFF>>(unitStatus +1));
			/* 更新表头 */
			Part.partFlash->writeFlashCB(writePageAddr, (uint8_t*)&pageHead,\
					sizeof(pageHead));
			/* 返回写入成功 */
			free(crcValueBuff);
			return FLASH_NO_ERR;
		}
		/* 3.3 前后CRC校验不相等写入失败，写下一区域 */
		else
		{
			writeSite ++;
			writeAddr += Part.dataSize+CRC_BIT;
			badBlock ++;
			goto Write;
		}
	}
	/* 4. 当前页面已写满，换页 */
	else
	{
		/* 4.1 计算下一页的页表头地址和数据地址 */
		uint16_t surplusSpace = 0;			// 当前页未使用的空间
		uint32_t lastPageAddr = 0;			// 上一次页面地址
		surplusSpace = Part.partFlash->minEraseSize - \
				 Part.dataNum * (Part.dataSize + CRC_BIT) - sizeof(pageHead);	// 页内未使用的空余空间
		/* 4.2 重置writeSite、 writeAddr */
		writeSite = 1;
		lastPageAddr = writeAddr - sizeof(pageHead) - Part.dataNum * (Part.dataSize + CRC_BIT);
		writeAddr = writeAddr + surplusSpace + sizeof(pageHead);
		/* 4.3计算是否是最后一页 */
		if(writeAddr >= Part.partStartAddr +Part.partSize)
		{
			writeAddr = Part.partStartAddr + sizeof(pageHead);
		}
		/* 4.4 计算页头和页面地址写入数据 */
		writeDataAddr = writeAddr;
		writePageAddr = writeAddr - sizeof(pageHead);
		Part.partFlash->writeFlashCB(writeDataAddr, crcValueBuff, Part.dataSize + CRC_BIT);
		/* 4.5 读出数据再次进行CRC校验，获取读出后校验值 */
		Part.partFlash->readFlashCB(writeDataAddr, crcValueBuff, Part.dataSize + CRC_BIT);
		crcLaterValue = Crc16(crcValueBuff, Part.dataSize);
		/* 4.6 前后CRC校验相等，更新索引表 */
		if(crcAgoValue == crcLaterValue)
		{
			badBlock = 0;
			Part.partFlash->readFlashCB(writePageAddr, (uint8_t*)&pageHead,\
					sizeof(pageHead));          // 读出页头，覆盖pageHead变量
			pageHead.pageStatus = PAGE_IN_USE;
			pageHead.dataIndex [0] = 0x7FFFFFFF;
			Part.partFlash->writeFlashCB(writePageAddr, (uint8_t*)&pageHead,\
					sizeof(pageHead));  		// 更新pageHead,写入页头
			/* 将老页面擦除并设置为free */
			Part.partFlash->eraseFlashCB(lastPageAddr, Part.partFlash->minEraseSize);
			pageHead.pageStatus = PAGE_FREE;
			memset(pageHead.dataIndex, 0xFFFFFFFF, sizeof(pageHead.dataIndex));
			/* 数据校验通过更新表头 */
			Part.partFlash->writeFlashCB(lastPageAddr, (uint8_t*)&pageHead,\
					sizeof(pageHead));  		// 更新pageHead,写入页头
			/* 返回写入成功 */
			free(crcValueBuff);
			return FLASH_NO_ERR;
		}
		/* 4.7 前后CRC校验不相等，更换下一区域 */
		else
		{
			writeSite ++;
			writeAddr += Part.dataSize;
			badBlock ++;
			goto Write;
		}

	}

}



